using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace _1945
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Shooter : Microsoft.Xna.Framework.DrawableGameComponent
    {   
        #region Variable Declarations
        
        private SpriteBatch spriteBatch;
        private SpriteFont spriteFont;
        private SpriteFactory spriteFactory;
        private Texture2D gameSpriteSheet;
        private KeyboardState keyboard;
        private Player player;
        private Rectangle gameScreenBounds;

        private EnemySpawner enemySpawner;

        private SoundEffect explosion1;
        private SoundEffect bulletfire;
        
        private List<GameObject> playerBullets = new List<GameObject>();
        private List<GameObject> enemyBullets = new List<GameObject>();
        private List<Enemy> enemies = new List<Enemy>();
        private List<Powerup> powerups = new List<Powerup>();
        private List<GameObject> islands = new List<GameObject>();

        ScrollingBackground waterBackground;

        Random rand = new Random((int)DateTime.Now.Ticks);
        #endregion

        public Shooter(Game game, Texture2D gameSpriteSheet, ScrollingBackground waterBackground, SoundEffect explosion, SoundEffect bulletFire) 
            :base(game)
        {
            spriteBatch = (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));
            spriteFont = (SpriteFont)Game.Services.GetService(typeof(SpriteFont));
            spriteFactory = (SpriteFactory)Game.Services.GetService(typeof(SpriteFactory));
            gameScreenBounds = new Rectangle(0, 0, Game.Window.ClientBounds.Width, Game.Window.ClientBounds.Height);

            enemySpawner = new EnemySpawner(game, enemies, enemyBullets, islands, gameSpriteSheet, gameScreenBounds);

            this.explosion1 = explosion;
            this.bulletfire = bulletFire;

            this.gameSpriteSheet = gameSpriteSheet;
            this.waterBackground = waterBackground;

            player = new Player(game, gameSpriteSheet, new Vector2(gameScreenBounds.Width/2, gameScreenBounds.Height-40), new Vector2(4), 3, 5, Vector2.Zero, spriteFactory.getPlayerSprite(SpriteFactory.player.bigShipWithShadow) , gameScreenBounds, spriteFactory.getBulletSprite(SpriteFactory.bullets.updouble) ,playerBullets);

            //islands.Add(new GameObject(game, gameSpriteSheet, new Vector2(gameScreenBounds.Width * 2/3, -40), Vector2.One, new Vector2(0, 1), spriteFactory.getIslandSprite(SpriteFactory.islands.rockyIsland), gameScreenBounds));
            
        }


        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            keyboard = Keyboard.GetState();
            
            if (keyboard.IsKeyDown(Keys.Escape))
            {
                //Game.Exit();
                ((Game1)Game).setGameScene(Game1.GameScenes.startScene);
            }


            //Update enemyspawner
            enemySpawner.Update(gameTime);
            

            //Update player
            player.Update(gameTime);

            
            //Update playerBullets
            foreach(GameObject bullet in playerBullets){
                bullet.Update(gameTime);
            }


            //Update enemyBullets
            foreach (GameObject bullet in enemyBullets)
            {
                bullet.Update(gameTime);
            }


            //update enemies
            foreach (Enemy enemy in enemies)
            {
                enemy.Update(gameTime, player.Position, player.Width);
            }

            //update powerups
            foreach (Powerup powerup in powerups)
            {
                powerup.Update(gameTime, player.Position, player.Width);
            }
 
            //update islands
            foreach (GameObject island in islands)
            {
                island.Update(gameTime);
            }


            //remove exploded enemies 
            for (int i = 0; i < enemies.Count; i++ )
            {
                
                if (enemies[i].HasExploded)
                {
                    if (enemies[i].IsBoss)
                    {
                        HighScoreManager.writeScore(player.Score);
                        ((Game1)Game).gameEnded(HighScore.titleMessage.victory);
                    }
                    else
                    {
                        enemies.RemoveAt(i);
                        i--;
                    }
                }

            }
            

            #region Remove gameobjects offscreen
      
            //remove enemies moved off screen
            for (int i = 0; i < enemies.Count; i++)
            {
                if (enemies[i].isOffScreen())
                {
                    enemies.RemoveAt(i);
                    i--;
                }
            }
            

            //remove playerbullets off screen
            for (int i = 0; i < playerBullets.Count; i++)
            {
                if (playerBullets[i].isOffScreen()){
                    playerBullets.RemoveAt(i);
                    i--;
                }
            }


            //remove enemyBullets off screen
            for (int i = 0; i < enemyBullets.Count; i++)
            {
                if (enemyBullets[i].isOffScreen())
                {
                    enemyBullets.RemoveAt(i);
                    i--;
                }
            }


            //remove islands off screen
            for (int i = 0; i < islands.Count; i++)
            {
                if (islands[i].isOffScreen())
                {
                    islands.RemoveAt(i);
                    i--;
                }
            }

            #endregion

            #region Collision detection
            
            //Playerbullets <-> enemy
            for (int i = 0; i < playerBullets.Count; i++)
            {
                for (int j = 0; j < enemies.Count; j++)
                {   
                    
                    if (i >= 0 && playerBullets.ElementAt(i).checkCollision(enemies.ElementAt(j).BoundingRectangle) && !enemies.ElementAt(j).IsExploding)
                    {
                        player.updateScore(enemies[j].Score); //score of (future) enemy class-instance
                        playerBullets.RemoveAt(i);
                        //enemies[j].explode(gameTime);   
                        enemies[j].damage(player.BulletDamage, gameTime);
                        i--;
                        explosion1.Play();

                        //random powerup spawn
                        if (rand.Next(1, 21) == 10)
                        {
                            Powerup powerup = new Powerup(Game, gameSpriteSheet, enemies[j].Position, new Vector2(0.3f, 0.3f), spriteFactory.getPowerupSprite(((SpriteFactory.powerups)(rand.Next(1, 6)))), gameScreenBounds, new MovementPattern(((MovementPattern.DirectionalPattern)rand.Next(1, 6))));
                            powerups.Add(powerup);
                        }
                    }
                }  
            }
            

            //enemyBullets <-> player
            for (int i = 0; i < enemyBullets.Count; i++)
            {
                if (enemyBullets.ElementAt(i).checkCollision(player.BoundingRectangle) && !player.IsExploding && !player.IsInvincible)
                {
                    enemyBullets.RemoveAt(i);
                    //player.explode(gameTime);
                    player.damage(30, gameTime);
                    i--;
                    bulletfire.Play();
                    //explosion1.Play();
                }
            }
            

            //player <-> enemy
            for (int i = 0; i < enemies.Count; i++)
            {
                
                if (enemies.ElementAt(i).checkCollision(player.BoundingRectangle) && !player.IsExploding && !player.IsInvincible)
                {
                    //player and enemy collided
                    //player.explode(gameTime);
                    player.damage(50, gameTime);
                    //enemies[i].damage(50, gameTime);
                    enemies[i].explode(gameTime);
                    bulletfire.Play();
                }

            }
            

            //player <-> powerup
            for (int i = 0; i < powerups.Count; i++)
            {

                if (powerups.ElementAt(i).checkCollision(player.BoundingRectangle) && !player.IsExploding)
                {
                    if (rand.Next(0, 2) == 1)
                    {
                        player.addBulletPower(10);
                    }
                    else
                    {
                        player.addHealth(10);
                    }
                    powerups.RemoveAt(i);
                    i--;
                }
            }
            #endregion

            //random island spawn
            if (islands.Count == 0 && rand.Next(1, 40) == 3)
            {
                islands.Add(new GameObject(Game, gameSpriteSheet, new Vector2(rand.Next(0, gameScreenBounds.Width - 32), -32), Vector2.One, new Vector2(0, 1), spriteFactory.getIslandSprite((SpriteFactory.islands)(rand.Next(1, 4))), gameScreenBounds));
            }

            //end game criteria
            if (player.Lives <= 0 && !player.IsExploding)
            {
                //game over
                HighScoreManager.writeScore(player.Score);
                ((Game1)Game).gameEnded(HighScore.titleMessage.gameOver);
            }


            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Draw(GameTime gameTime)
        {
            
            waterBackground.Draw(spriteBatch, Game.Window);

            spriteBatch.Begin();
            
            foreach (GameObject island in islands)
            {
                island.Draw(gameTime);
            }

            player.Draw(gameTime);

            foreach (GameObject bullet in playerBullets)
            {
                bullet.Draw(gameTime);
            }

            
            foreach (Enemy enemy in enemies)
            {
                enemy.Draw(gameTime);

            }
            
              
            foreach (GameObject powerup in powerups)
            {
                powerup.Draw(gameTime);
            }


            foreach (GameObject bullet in enemyBullets)
            {
                bullet.Draw(gameTime);
            }

            //spriteBatch.DrawString(spritefont, "Player collision: " + player.checkCollision(enemy2), new Vector2(200, 0), Color.White);
            //spriteBatch.DrawString(spriteFont, "Enemies: " + enemies.Count.ToString(), new Vector2(200, 0), Color.White);
            //spriteBatch.DrawString(spriteFont, "Enemy bullets: " + enemyBullets.Count.ToString(), new Vector2(320, 0), Color.White);
            // End rendering sprites
            spriteBatch.End();

        }
    }
}
